package com.atguigu.bean;

import com.atguigu.anno.Bean;
import com.atguigu.anno.Di;

import java.io.File;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.net.FileNameMap;
import java.net.URL;
import java.net.URLDecoder;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

public class AnnotationApplicationContext implements ApplicationContext {

    //创建map集合，放bean对象
    private static Map<Class, Object> beanFactory = new HashMap<>();
    private static String rootPath;

    //返回对象
    @Override
    public Object getBean(Class clazz) {
        return beanFactory.get(clazz);
    }

    //设置包扫描规则
    //当前包及其子包，哪个类有@bean注解，把这个类通过反射实例化
    //创建有参数构造，传递包路径，设置包扫描规则
    public AnnotationApplicationContext(String packageName) throws ClassNotFoundException, InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException {
        //1.获取包下的所有类
        //2 获取包绝对路径
        //1 把.替换成\
        String packagePath = packageName.replaceAll("\\."
                , "\\\\");
        try {

            Enumeration<URL> urls
                    = Thread.currentThread()
                    .getContextClassLoader()
                    .getResources(packagePath);
            while (urls.hasMoreElements()) {
                URL url = urls.nextElement();
                String path = url.getPath();
                String filePath = URLDecoder.decode(url.getFile()
                        , "utf-8");
                System.out.println(filePath);
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }


        String filePath = AnnotationApplicationContext.class.getResource("/").getPath() + packagePath;

        //获取包前面路径部分，字符串截取
        rootPath = filePath.substring(0, filePath.length() - packagePath.length());
        loadBean(new File(filePath));
        //属性注入
        loadDi();
    }

    //属性注入
    private void loadDi() {
        // 实例化对象在beanFactory的map集合里面
        // 1 遍历beanFactory的map集合，

        Set<Map.Entry<Class, Object>> entries = beanFactory.entrySet();
        for (Map.Entry<Class, Object> entry : entries) {
            // 2 获取map集合每个对象（value），
            Object obj = entry.getValue();

            //获取对象Class
            Class<?> clazz = obj.getClass();
            //获取每个对象属性获取到
            Field[] declaredFields = clazz.getDeclaredFields();
            // 3 遍历得到每个对象属性数组，得到每个属性
            for (Field field : declaredFields) {
                // 4 判断属性上面是否有 @Di注解
                if (field.isAnnotationPresent(Di.class)) {
                    Di annotation = field.getAnnotation(Di.class);
                    //如果私有属性，设置可以设置值

                    if(annotation!=null){
                        field.setAccessible(true);
                        // 5 如果有@Di注解，把对象设置到属性上
                        try {
                            field.set(obj,beanFactory.get(field.getType()));
                        } catch (IllegalAccessException e) {
                            throw new RuntimeException(e);
                        }
                    }
                    // 5.1 获取属性的类型
                }
            }
        }
    }

    //包扫描过程，实例化
    private static void loadBean(File file) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        //1.判断是否是文件夹
        if (file.isDirectory()) {
            //2.获取文件夹里面所有内容
            File[] childrenFiles = file.listFiles();
            //3 判断文件夹里面为空，直接返回
            if (childrenFiles == null || childrenFiles.length == 0) {
                return;
            }
            //4 如果文件夹里面不为空，遍历文件夹所有内容
            for (File child : childrenFiles) {
                //4.1 遍历得到每个File对象，继续判断，如果还是文件夹，递归
                if (child.isDirectory()) {
                    //递归
                    loadBean(child);
                } else {
                    //4.2 遍历得到File对象不是文件夹，是文件
                    //4.3 得到包路径+类名称部分-字符串截取
                    String pathWithClass =
                            child.getAbsolutePath().substring(rootPath.length() - 1);

                    //4.4 判断当前文件类型是否.class
                    if (pathWithClass.contains(".class")) {
                        //4.5 如果是.class，判断类上是否有@bean注解
                        //com.atguigu.service.UserServiceImpl
                        String allName = pathWithClass
                                .replaceAll("\\\\", ".")
                                .replace(".class", "");
                        //4.6 如果有@bean注解，通过反射实例化
                        //4.6.1 获取类的class
                        //4.6.2 判断不是接口
                        Class<?> clazz = Class.forName(allName);
                        if (!clazz.isInterface()) {
                            //4.6.3 判断类上面是否有注解@Bean
                            Bean annotation = clazz.getAnnotation(Bean.class);
                            if (annotation != null) {
                                //4.6.4 通过反射实例化
                                Object instance = clazz.getConstructor().newInstance();
                                //4.7 把实例化的对象放到map集合中beanFactory
                                //4.7.1 判断当前类如果有接口，让接口class作为map的key
                                if (clazz.getInterfaces().length > 0) {
                                    beanFactory.put(clazz.getInterfaces()[0], instance);
                                } else {
                                    beanFactory.put(clazz, instance);
                                }
                            }
                            //4.6.4
                        }
                    }
                }
            }
        }
    }

    public static void main(String[] args) {
//        ApplicationContext context=new AnnotationApplicationContext("com.atguigu.bean");
//        context.getBean();
        //   pathDemo1("com.atguigu");
    }
}
